package chess

import (
	"fmt"
	"sync"

	"gddgame.cc/galaxy/common/feature/orbit"
	"gddgame.cc/galaxy/common/feature/orbit/chess_protocol"
	"gddgame.cc/galaxy/utils"
)

type DataManager struct {
	DataType byte
	Datas    DataSlice
	Idle     []int
	IndexMap sync.Map
	Moving   sync.Map

	SaveType byte

	block *orbit.BlockLocation
}

func NewDataManager(dataType byte, block *orbit.BlockLocation) *DataManager {
	data := &DataManager{
		DataType: dataType,
		Datas:    []*chess_protocol.SingleData{},
		Idle:     []int{},
		IndexMap: sync.Map{},
		Moving:   sync.Map{},
		block:    block,
	}

	return data
}

func (dm *DataManager) Message(message *chess_protocol.SendMessage) *chess_protocol.SingleData {
	//fmt.Println("update", data)
	index, ok := dm.index(message.Id)
	if !ok {
		return nil
	}
	d := dm.Datas[index]
	d.SetMessage(message.Message)
	return d
}

func (dm *DataManager) Add(data *chess_protocol.SingleData) *chess_protocol.SingleData {
	// 对于非持久化消息，在add时移除
	// 因为要支持直接创建带message，外部通过byte[]直接下发带message的数据
	// 所以在add时移除，避免逻辑错误
	if data.Data.Message != nil && !data.Data.Message.Persistent {
		data.Data.Message = nil
	}
	dm.SetIndex(data)
	return data
}

func (dm *DataManager) Update(id interface{}, data *chess_protocol.UpdatePublicData) (*chess_protocol.SingleData, int) {
	d, index := dm.get(id)
	if d == nil {
		return nil, index
	}
	d.Data.UpdateProp(data.Props)
	d.Data.UpdateExtend(data.Extend)
	d.Data.ResetExtend(data.Reset)
	d.Data.UpdateListId(data.ListId)
	d.Data.UpdateListNo(data.ListNo)
	d.Data.UpdateListNode(data.ListNode)
	d.SetMessage(data.Message)
	return d, index
}
func (dm *DataManager) UpdateMove(data *chess_protocol.MoveData) (*chess_protocol.SingleData, int) {
	//fmt.Println("update", data)
	d, index := dm.Update(data.Id, &data.Data)
	if d == nil {
		return nil, index
	}
	d.Data.Position = data.Current
	return d, index
}
func (dm *DataManager) UpdateNode(data *chess_protocol.UpdateNode) *chess_protocol.SingleData {
	//fmt.Println("update", data)
	d, _ := dm.Update(data.Id, &data.Data)
	if d == nil {
		return nil
	}
	d.Data.Range = data.Range
	return d
}

func (dm *DataManager) Move(id interface{}) {
	switch t := id.(type) {
	case string:
		dm.Moving.Store(t, struct{}{})
	case int:
		dm.Moving.Store(t, struct{}{})
	default:
	}
}

func (dm *DataManager) InMoving(id interface{}) []byte {
	d, _ := dm.get(id)
	if d == nil {
		return nil
	}
	var ok bool
	switch t := id.(type) {
	case string:
		_, ok = dm.Moving.Load(t)
	case int:
		_, ok = dm.Moving.Load(t)
	default:
	}
	if !ok {
		return nil
	}
	return d.Data.Position
}

func (dm *DataManager) Remove(id interface{}) {
	var ok bool
	switch t := id.(type) {
	case string:
		_, ok = dm.Moving.Load(t)
		if ok {
			dm.Moving.Delete(t)
		}
	case int:
		_, ok = dm.Moving.Load(t)
		if ok {
			dm.Moving.Delete(t)
		}
	case int64:
		_, ok = dm.Moving.Load(int(t))
		if ok {
			dm.Moving.Delete(int(t))
		}
	default:
		ok = true
	}
	if !ok {
		return
	}
	dm.Delete(id)
}

func (dm *DataManager) Delete(id interface{}) *chess_protocol.SingleData {
	//fmt.Println("delete", id)
	//debug.PrintStack()
	index, ok := dm.index(id)
	if !ok {
		return nil
	}
	switch t := id.(type) {
	case []byte:
		ii := orbit.LocationCM.IndexInBlock(dm.block, t)
		fmt.Println("delte index", ii, index, dm.Datas[index])
		dm.IndexMap.Delete(ii)
	case string:
		dm.IndexMap.Delete(t)
		dm.Idle = append(dm.Idle, index)
	default:
		dm.IndexMap.Delete(t)
		dm.Idle = append(dm.Idle, index)
	}
	d := dm.Datas[index]
	dm.Datas[index] = nil
	return d
}

func (dm *DataManager) index(id interface{}) (int, bool) {
	switch ii := id.(type) {
	case []byte:
		index := orbit.LocationCM.IndexInBlock(dm.block, ii)
		i, ok := dm.IndexMap.Load(index)
		if !ok {
			nn := &chess_protocol.SingleData{
				DataType: dm.DataType,
				Id:       id,
				Data: &chess_protocol.Data{
					Position: ii,
					Extend:   [][]chess_protocol.ExtendItem{},
					ListId:   [][]string{},
					ListNo:   [][]int{},
					ListNode: [][][]byte{},
				},
			}
			i = len(dm.Datas)
			dm.IndexMap.Store(index, i)
			dm.Datas = append(dm.Datas, nn)
		}
		return utils.ToInt(i), true
	case string:
		//fmt.Println("get strring", ii)
		i, ok := dm.IndexMap.Load(ii)
		if !ok {
			//fmt.Println("index miss string", dm.DataType, ii, dm.block, id)
			return -1, false
		}
		return utils.ToInt(i), true
	default:
		//fmt.Println("get innt", ii)/**/
		i, ok := dm.IndexMap.Load(ii)
		if !ok {
			fmt.Println("index miss int", dm.DataType, ii, dm.block, id)
			return -1, false
		}
		return utils.ToInt(i), true
	}
}

func (dm *DataManager) get(id interface{}) (*chess_protocol.SingleData, int) {
	index, ok := dm.index(id)
	if !ok {
		//fmt.Println("index miss", dm.DataType, id, dm.block)
		//debug.PrintStack()
		return nil, -1
	}
	d := dm.Datas[index]
	if d == nil {
		//fmt.Println("data miss", dm.DataType, id, index, dm.block)
		return nil, index
	}
	return d, index
}

func (dm *DataManager) SetIndex(data *chess_protocol.SingleData) {
	switch ii := data.Id.(type) {
	case []byte:
		index := orbit.LocationCM.IndexInBlock(dm.block, ii)
		n, ok := dm.IndexMap.Load(index)
		//fmt.Println("create node", len(dm.NodeIndex), len(dm.Nodes), index, n)
		if !ok {
			dm.IndexMap.Store(index, len(dm.Datas))
			dm.Datas = append(dm.Datas, data)
		} else {
			index = utils.ToInt(n)
			dm.Datas[index] = data
		}
	case string:
		i, ok := dm.IndexMap.Load(ii)
		//fmt.Println("create string", data.Id, ii, i)
		if !ok {
			index := len(dm.Datas)
			l := len(dm.Idle)
			if l > 0 {
				index = dm.Idle[0]
				if l > 1 {
					dm.Idle = dm.Idle[1:]
				} else {
					dm.Idle = dm.Idle[0:0]
				}
				dm.Datas[index] = data
			} else {
				dm.Datas = append(dm.Datas, data)
			}
			//fmt.Println("set index", data.Id, ii, index)
			dm.IndexMap.Store(ii, index)
		} else {
			index := utils.ToInt(i)
			dm.Datas[index] = data
			//fmt.Println("set index", ii, index)
		}
	default:
		i, ok := dm.IndexMap.Load(ii)
		//fmt.Println("create int", data.Id, reflect.TypeOf(ii).Name(), i)
		if !ok {
			index := len(dm.Datas)
			l := len(dm.Idle)
			if l > 0 {
				index = dm.Idle[0]
				if l > 1 {
					dm.Idle = dm.Idle[1:]
				} else {
					dm.Idle = dm.Idle[0:0]
				}
				dm.Datas[index] = data
			} else {
				dm.Datas = append(dm.Datas, data)
			}
			//fmt.Println("set index", data.Id, ii, index)
			dm.IndexMap.Store(ii, index)
		} else {
			index := utils.ToInt(i)
			dm.Datas[index] = data
			//fmt.Println("set index", ii, index)
		}
	}
}
